package com.hailiao.beidou.message.bd

import com.d10ng.compose.model.UiViewModelManager
import com.d10ng.datelib.curTime
import com.hailiao.beidou.BeidouSDK
import com.hailiao.beidou.comm.bluetooth.BLEHub
import com.hailiao.beidou.comm.bluetooth.constant.BLEStatus
import com.hailiao.beidou.comm.serialport.SPDeviceManager
import com.hailiao.beidou.comm.serialport.SPHub
import com.hailiao.beidou.comm.serialport.SPWire
import com.hailiao.beidou.core.BDCore
import com.hailiao.beidou.core.model.BDHub
import com.hailiao.beidou.message.bd.ble.BLEDeviceManager
import com.hailiao.beidou.message.bd.constant.BDStatus
import com.hailiao.beidou.message.bd.constant.ProtoVersion
import com.hailiao.beidou.message.bd.constant.TextType
import com.hailiao.beidou.message.bd.data.BDCardData
import com.hailiao.beidou.message.bd.data.BDDeviceStatusData
import com.hailiao.beidou.message.bd.data.toBDCardData
import com.hailiao.beidou.message.bd.data.toBDDeviceStatusDataList
import com.hailiao.beidou.message.bd.task.CheckCardTask
import com.hailiao.beidou.message.bd.task.CheckSignalTask
import com.hailiao.beidou.standard2.BDBSI_Data
import com.hailiao.beidou.standard2.BDICI_Data
import com.hailiao.beidou.standard2.CCTXA_Data
import com.hailiao.beidou.standard2.CCWBA_Data
import com.hailiao.beidou.standard2.DW_HeightMode
import com.hailiao.beidou.standard2.sendCCTXA
import com.hailiao.beidou.standard2.sendCCWBA
import com.hailiao.beidou.standard3.BDFKI_Data
import com.hailiao.beidou.standard3.BDICP_Data
import com.hailiao.beidou.standard3.BDPWI_Data
import com.hailiao.beidou.standard3.CCEPQ_Data
import com.hailiao.beidou.standard3.CCRMO_Data
import com.hailiao.beidou.standard3.CCTCQ_Data
import com.hailiao.beidou.standard3.EPQ_InboundSpeed
import com.hailiao.beidou.standard3.EPQ_Mode
import com.hailiao.beidou.standard3.EPQ_SearchAndRescueType
import com.hailiao.beidou.standard3.EP_EmergencyTag
import com.hailiao.beidou.standard3.FKI_Code
import com.hailiao.beidou.standard3.FKI_Mode
import com.hailiao.beidou.standard3.RMO_Action
import com.hailiao.beidou.standard3.constant.BDSendChannel
import com.hailiao.beidou.standard3.sendCCEPQ
import com.hailiao.beidou.standard3.sendCCRMO
import com.hailiao.beidou.standard3.sendCCTCQ
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.Timer
import kotlin.concurrent.schedule
import kotlin.math.ceil
import kotlin.math.max
import com.hailiao.beidou.standard2.BDFKI_Data as BDFKI2Data
import com.hailiao.beidou.standard2.FKI_Code as FKI2Code

/**
 * 北斗通讯
 * @Author d10ng
 * @Date 2024/3/1 15:15
 */
object Beidou {

    // 北斗状态
    val statusFlow = MutableStateFlow(BDStatus.DISCONNECT)
    // 北斗信号值状态
    val signalValueFlow = MutableStateFlow<List<Pair<Int, Int>>>(listOf())
    // 等待下一次能发送的时间，秒
    val waitTimeFlow = MutableStateFlow(0)
    // 下一次能发送消息的时间戳，单位毫秒
    val nextSendTimeFlow = MutableStateFlow(curTime)
    // 北斗卡信息
    val cardFlow = MutableStateFlow(BDCardData())

    // 通讯通道，如果设备为手持机，则为串口通讯，否则为蓝牙通讯
    var hub: BDHub = if (SPDeviceManager.getType() == null) BLEHub else SPHub
        private set

    init {
        // 初始化协议
        BeidouSDK.install()
        val scope = CoroutineScope(Dispatchers.IO)
        // 监听蓝牙连接状态
        scope.launch {
            BLEHub.statusFlow.collect {
                if (hub == SPHub) return@collect
                statusFlow.value = when (it) {
                    BLEStatus.CONNECTED -> BDStatus.CONNECTING
                    else -> BDStatus.DISCONNECT
                }
            }
        }
        // 监听接收协议
        scope.launch {
            BDCore.receiveProtoSharedFlow.collect {
                when(val data = it.protocol) {
                    // 信号状态
                    is BDPWI_Data -> onReceiveSignalStatus(data)
                    is BDBSI_Data -> onReceiveSignalStatus(data)
                    // 卡号信息
                    is BDICP_Data -> onReceiveCardInfo(data)
                    is BDICI_Data -> onReceiveCardInfo(data)
                    // 执行短报文发送返回
                    is BDFKI_Data -> onReceiveSendResult(data)
                    is BDFKI2Data -> onReceiveSendResult(data)
                }
            }
        }
        // 监听设备连接状态
        scope.launch {
            statusFlow.collect {
                if (it == BDStatus.DISCONNECT) {
                    nextSendTimeFlow.value = curTime
                    // 关闭所有定时任务
                    CheckCardTask.stop()
                    CheckSignalTask.stop()
                }
            }
        }
        // 监听下一次可发送时间变化
        scope.launch {
            nextSendTimeFlow.collect {
                updateWaitTime()
            }
        }
        // 定时更新等待时间
        Timer().schedule(1000L, 100L) {
            updateWaitTime()
        }
    }

    /**
     * 设置日志输出
     * @param value Boolean
     */
    fun setDebug(value: Boolean) {
        BDCore.setDebug(value)
        BLEHub.setDebug(value)
        SPHub.setDebug(value)
    }

    /**
     * 设置通讯通道
     * @param value BDHub
     */
    fun setHub(value: BDHub) {
        if (hub == value) return
        disconnect()
        hub = value
    }

    /**
     * 连接设备后触发初始化
     * @param ver ProtoVersion
     */
    suspend fun onDeviceConnected(ver: ProtoVersion) {
        // 关闭所有输出
        hub.sendCCRMO(CCRMO_Data(action = RMO_Action.CLOSE_ALL))
        // 启动定时任务
        CheckCardTask.start(ver)
        CheckSignalTask.start(ver)
    }

    /**
     * 断开连接
     */
    fun disconnect() {
        hub.disconnectAll()
        statusFlow.value = BDStatus.DISCONNECT
    }

    /**
     * 连接串口设备
     * @param wire SPWire
     * @return Boolean
     */
    fun connectSP(wire: SPWire): Boolean {
        return runBlocking { SPHub.connect(wire) }
    }

    /**
     * 获取设备状态信息FLOW
     * @return Flow<List<BDDeviceStatusData>>?
     */
    fun getDeviceStatusFlow(): Flow<List<BDDeviceStatusData>>? {
        return if (hub == SPHub) {
            cardFlow.map { it.toBDDeviceStatusDataList() }
        } else {
            BLEDeviceManager.getConnectedDevice()?.getDeviceStatusDataListFlow()
        }
    }

    /**
     * 判断是否已连接
     * @return Boolean
     */
    fun isConnected(): Boolean {
        return statusFlow.value.isConnected
    }

    /**
     * 发送消息
     * @param ver ProtoVersion
     * @param number String
     * @param content String
     * @param type TextType
     * @return Boolean
     */
    suspend fun sendMessage(
        ver: ProtoVersion,
        number: String,
        content: String,
        type: TextType
    ): Boolean {
        if (hub == BLEHub) {
            BLEDeviceManager.getConnectedDevice()?.setCenterCard(number)
        }
        return when(ver) {
            ProtoVersion.BD3 -> hub.sendCCTCQ(CCTCQ_Data(
                address = number,
                codeType = type.value3,
                content = content
            ))?.isSuccess ?: false
            ProtoVersion.BD2 -> hub.sendCCTXA(CCTXA_Data(
                address = number,
                type = type.value2,
                content = content
            ))?.isSuccess ?: false
        }
    }

    /**
     * 发送位置
     * @param ver ProtoVersion
     * @param number String
     * @return Boolean
     */
    suspend fun sendLocation(
        ver: ProtoVersion,
        number: String
    ): Boolean {
        return when(ver) {
            ProtoVersion.BD3 -> hub.sendCCEPQ(CCEPQ_Data(
                address = number,
                mode = EPQ_Mode.AUTO,
                channel = BDSendChannel.LF1,
                inboundSpeed = EPQ_InboundSpeed.L1,
                emergencyTag = EP_EmergencyTag.STANDARD_POSITIONING_REPORT,
                altitude = 0.0,
                searchAndRescueType = EPQ_SearchAndRescueType.NONE,
                frequency = 0,
                inboundPower = 100
            ))?.isSuccess ?: false
            ProtoVersion.BD2 -> hub.sendCCWBA(CCWBA_Data(
                receiverAddress = number,
                heightMode = DW_HeightMode.LOW,
                antennaeHeight = 0.0,
                frequency = 0,
            ))?.isSuccess ?: false
        }
    }

    /**
     * 更新等待时间
     */
    private fun updateWaitTime() {
        val next = nextSendTimeFlow.value
        val cur = curTime
        if (next > cur) {
            waitTimeFlow.value = ceil((next - cur) / 1000.0).toInt()
        } else {
            waitTimeFlow.value = 0
        }
    }

    /**
     * 接收信号状态数据进行更新
     * @param data BDPWI_Data
     */
    private fun onReceiveSignalStatus(data: BDPWI_Data) {
        statusFlow.value = BDStatus.parse(data)
        signalValueFlow.value = data.newBeamStatusList.map { it.id to max(it.s2cdCnr, it.s2cpCnr) }
        // 定时清除信号状态
        startClearSignalStatusJob()
    }

    /**
     * 接收信号状态数据进行更新
     * @param data BDBSI_Data
     */
    private fun onReceiveSignalStatus(data: BDBSI_Data) {
        statusFlow.value = BDStatus.parse(data)
        signalValueFlow.value = data.beamPowerList.mapIndexed { index, item -> index to item * 15 }
        // 定时清除信号状态
        startClearSignalStatusJob()
    }

    private var clearSignalStatusJob: Job? = null
    private fun startClearSignalStatusJob() {
        clearSignalStatusJob?.cancel()
        clearSignalStatusJob = CoroutineScope(Dispatchers.IO).launch {
            delay(5000)
            if (statusFlow.value != BDStatus.DISCONNECT) {
                statusFlow.value = BDStatus.NONE
            }
            signalValueFlow.value = listOf()
        }
    }

    /**
     * 接收卡号数据进行更新
     * @param data BDICP_Data
     */
    private fun onReceiveCardInfo(data: BDICP_Data) {
        cardFlow.value = data.toBDCardData()
    }

    /**
     * 接收卡号数据进行更新
     * @param data BDICI_Data
     */
    private fun onReceiveCardInfo(data: BDICI_Data) {
        cardFlow.value = data.toBDCardData()
    }

    /**
     * 接收发送结果
     * @param data BDFKI_Data
     */
    private fun onReceiveSendResult(data: BDFKI_Data) {
        if (data.waitTime > 0) {
            nextSendTimeFlow.value = curTime + data.waitTime * 1000
        } else if (data.isSuccess) {
            nextSendTimeFlow.value = curTime + cardFlow.value.frequency * 1000
        }
        if (
            data.isSuccess.not()
            && (data.mode == FKI_Mode.TCQ || data.mode == FKI_Mode.EPQ)
            && data.code != FKI_Code.NONE
            )
        {
            val sb = StringBuilder("北斗短报文发送失败").apply {
                if (data.code != FKI_Code.ERROR_UNKNOWN) {
                    append("，${data.code.text}")
                }
            }
            // 发送失败
            UiViewModelManager.showErrorNotify(sb.toString())
        }
    }

    /**
     * 接收发送结果
     * @param data BDFKI_Data
     */
    private fun onReceiveSendResult(data: BDFKI2Data) {
        if (data.waitTime > 0) {
            nextSendTimeFlow.value = curTime + data.waitTime * 1000
        } else if (data.isSuccess) {
            nextSendTimeFlow.value = curTime + cardFlow.value.frequency * 1000
        }
        if (
            data.isSuccess.not()
            && data.name == "TXA" || data.name == "WBA"
            && data.code != FKI2Code.SUCCESS
            )
        {
            val sb = StringBuilder("北斗短报文发送失败").apply {
                if (data.code != FKI2Code.OTHER) {
                    append("，${data.code.text}")
                }
            }
            // 发送失败
            UiViewModelManager.showErrorNotify(sb.toString())
        }
    }
}